function yld = xirr(cf, df, guess, maxiter, basis)
%XIRR Internal rate of return for non-periodic cash flows.
%
%   R = XIRR(CF, DF)
%   R = XIRR(CF, DF, GUESS, MAXITER, BASIS)
%
%   Optional Inputs: GUESS, MAXITER, BASIS
%
%   Inputs:
%        CF - A vector or matrix of cash flows. If CF is a matrix, each column
%             represents a separate stream of cash flows whose internal rate of 
%             return is calculated. The first cash flow of each stream is the 
%             initial investment, usually entered as a negative number.
%
%        DF - A vector or matrix of serial date numbers the same size as CF, or
%             a cell array of date strings the same size as CF. Each column of 
%             DF represents the dates of the corresponding column of CF. 
%
%   Optional Inputs:
%     GUESS - The initial estimate of the internal rate of return. GUESS is a 
%             scalar applied to all streams, or a vector the same length as the 
%             number of streams. Default GUESS = 0.1.
%
%   MAXITER - The positive integer number of iterations used by Newton's method 
%             to solve for the internal rate of return. MAXITER is a scalar 
%             applied to all streams, or a vector the same length as the number 
%             of streams. Default MAXITER = 50.
%
%     BASIS - The day count basis. BASIS is a scalar applied to all streams, or 
%             a vector the same length as the number of streams. Default 
%             BASIS = 0 (actual/actual).
%
%   Outputs:
%         R - Vector of the annualized internal rate of return of each cash flow 
%             stream. A NaN indicates that a solution was not found.
%
%   Example:
%      An investment of $10,000 generates the following stream of cash flows.
%      Note that the original investment is the first cash flow and is entered 
%      as a negative number.
%
%             Cash Flow         Dates    
%              -10000        12-Jan-2007
%                2500        14-Feb-2008
%                2000        03-Mar-2008
%                3000        14-Jun-2008
%                4000        01-Dec-2008
%
%      The variables CF and DF are defined as follows.
%
%         CF = [-10000 ; 2500 ; 2000 ; 3000 ; 4000];
%         DF = {'12-Jan-2007'
%               '14-Feb-2008'
%               '03-Mar-2008'
%               '14-Jun-2008'
%               '01-Dec-2008'};
%
%          R = xirr(CF, DF)
%
%          R = 0.1006         (10.0644% per annum)
%
%   See also IRR, MIRR, PVVAR.
%
%   Reference: Sharpe and Alexander, Investments, 4th edition, page 463.

%   Copyright 1995-2008 The MathWorks, Inc.
%   $Revision: 1.10.2.6 $   $Date: 2009/05/07 18:23:35 $

%
% Validate required inputs.
%

if nargin < 2
   error('finance:xirr:tooFewInputs', 'Specify at least 2 inputs: cash flows and dates.')
end

if ~isnumeric(cf)
   error('finance:xirr:invalidInputArg','Invalid CF input.')
end

if ischar(df) || iscell(df)
   if iscell(df) && (size(df, 2) > 1)
      % Convert cell array of dates to char string so datenum can process.
      df = char(df);
   end

   df = datenum(df);

   try
      df = reshape(df, size(cf));
   catch
      error('finance:xirr:insufficientNumberOfDates', ...
            'Number of cash flows is inconsistent with number of dates.')
   end
end

[rowcf, colcf] = size(cf);  % colcf = # of cash flow streams.
[rowdf, coldf] = size(df);

if rowcf ~= rowdf || colcf ~= coldf
   error('finance:xirr:invalidDims', ...
         'CF and DF must be of the same dimensions.')
end

if rowcf == 1      % If inputs are row vectors,
   cf    = cf(:);  % flip them.
   df    = df(:);
   colcf = 1;
end

%
% Optional input default assignment & scalar expansion.
%

if (nargin < 3) || isempty(guess)
   guess = 0.1;  % Initial estimate of IRR.
end

if ~isnumeric(guess)
   error('finance:xirr:invalidInputArg','Invalid GUESS input.')
end

if (nargin < 4) || isempty(maxiter)
   maxiter = 50; % Maximum # of iterations.
end

if ~isnumeric(maxiter)
   error('finance:xirr:invalidInputArg','Invalid MAXITER input.')
end

if (nargin < 5) || isempty(basis)
   basis = 0;    % Day count basis.
end

if ~isnumeric(basis)
   error('finance:xirr:invalidInputArg','Invalid BASIS input.')
end

if numel(guess) == 1
   guess = guess(ones(1, colcf));
end

if numel(maxiter) == 1
   maxiter = maxiter(ones(1, colcf));
end

if numel(basis) == 1
   basis = basis(ones(1, colcf));
end

%
% Consistency between required & optional inputs.
%

if (numel(guess) ~= colcf)
    error('finance:xirr:incompatibleGuess', ...
          'GUESS must be a scalar or a vector compatible with CF.')
end

if (numel(maxiter) ~= colcf)
    error('finance:xirr:incompatibleMaxiter', ...
          'MAXITER must be a scalar or a vector compatible with CF.')
end

if (numel(basis) ~= colcf)
    error('finance:xirr:incompatibleBasis', ...
          'BASIS must be a scalar or a vector compatible with CF.')
end

yld = guess;  % Assign the initial IRR guess.
tol = 1e-6;   % Numerical tolerance
convFlag = zeros(1, colcf);
maxItFlag = zeros(1, colcf);

for loop = 1:colcf

%
%  Scale the current CF stream, avoiding divide by zero.
%
   cf(:,loop) = cf(:,loop) ./ max(mean(abs(cf(:,loop))), 1);
   n = length(df(:, loop));                                    % Number of cash flows
   f = floor(yearfrac(df(1, loop), df(n, loop), basis(loop))); % Number of years in cash flow
   if f == 0
      f = 1;
   end
%
%  Convert df to time factors in fractions of a year.
%
   delta1 = daysdif(df(1, loop), df(:, loop), basis(loop));
   delta2 = daysdif(df(1, loop), datemnth(df(1, loop), 12*f, 0, basis(loop)), basis(loop));
   tf     = f * delta1 / delta2;

   f = 2;                                            % Initialize loop parameters
   k = 1;
   while abs(f) > tol                               % Newton's method
      f = sum(cf(:, loop)./(1+yld(loop)).^tf);       % Cash flow polynomial
      fp = sum(-cf(:, loop)./((1+yld(loop)).^tf).*tf/(1+yld(loop)));
      if abs(fp) < tol
         convFlag(loop) = 1;
         yld(loop) = nan;
         break
      end
      del = -f/fp;
      yld(loop) = yld(loop)+del;
      k = k+1;                                       % Increment iteration count
      if k > maxiter(loop) 
         maxItFlag(loop) = 1;
         yld(loop) = nan;
         break
      end
   end

   if (abs(imag(yld(loop))) < tol)
      yld(loop) = real(yld(loop));
   else
      convFlag(loop) = 1;
      yld(loop) = NaN;
   end

end  % End for loop

ind = find(convFlag==1);
if (~isempty(ind))
   if (colcf==1)
      warning('finance:xirr:noIRR', 'Unable to find internal rate of return.');
   else
      warning('finance:xirr:noIRR',...
         'Unable to find internal rate of return for cash-flow streams: %s',...
         num2str(ind));
   end
end

ind = find(maxItFlag==1);
if (~isempty(ind))
   if (colcf==1)
      warning('finance:xirr:maxIterations', ...
         'Maximum iterations reached (increase MAXITER or try new GUESS).');
   else
      warning('finance:xirr:maxIterations', ...
         'Maximum iterations reached for cash-flow streams %s\n%s',...
         num2str(ind),'   (increase MAXITER or try new GUESS).');
   end
end
